//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using EffiProz.Core.Persist;


namespace EffiProz.Core
{

    /*
     * Parses a connection URL into parts.
     *
     * @author fredt@users
     * @version 1.8.0
     * @since 1.8.0
     */
    public class DatabaseURL
    {

        const string S_DOT = ".";
        public  const string S_MEM = "mem:";
        public  const string S_FILE = "file:";
        public  const string S_RES = "res:";
        public  const string S_ALIAS = "alias:";
        public  const string S_EFZ = "efz://";
        public  const string S_EFZS = "efzs://";
        public  const string S_HTTP = "http://";
        public  const string S_HTTPS = "https://";
        public  const string S_URL_PREFIX = "jdbc:hsqldb:";

        /**
         * Returns true if type represents an in-process connection to a file backed
         * database.
         */
        public static bool isFileBasedDatabaseType(string url)
        {

            if (url == S_FILE || url == S_RES)
            {
                return true;
            }

            return false;
        }

        /**
         * Returns true if type represents an in-process connection to database.
         */
        public static bool isInProcessDatabaseType(string url)
        {

            if (url == S_FILE || url == S_RES || url == S_MEM)
            {
                return true;
            }

            return false;
        }

        /**
         * Parses the url into components that are returned _in a properties
         * object. <p>
         *
         * The following components are isolated: <p>
         *
         * <ul>
         * url: the original url<p>
         * connection_type: a static string that indicate the protocol. If the
         * url does not begin with a valid protocol, null is returned by this
         * method instead of the properties object.<p>
         * host: name of host _in networked modes _in lowercase<p>
         * port: port number _in networked mode, or 0 if not present<p>
         * path: path of the resource on server _in networked modes,
         * / (slash) _in all cases apart from
         * servlet path which is / (slash) plus the name of the servlet<p>
         * database: database name. For memory, resource and networked modes,
         * this is returned _in lowercase, for file databases the original
         * case of characters is preserved. Returns empty string if name is not
         * present _in the url.<p>
         * for each protocol if port number is not _in the url<p>
         * Additional connection properties specified as key/value pairs.
         * </ul>
         * @return null returned if the part that should represent the port is not
         * an integer or the part for database name is empty.
         * Empty HsqlProperties returned if if url does not begin with valid
         * protocol and could refer to another JDBC driver.
         *
         */
        public static EffiProzProperties parseURL(string url, bool hasPrefix)
        {

            string urlImage = url.ToLower();
            EffiProzProperties props = new EffiProzProperties();
            EffiProzProperties extraProps = null;
            string arguments = null;
            int pos = 0;

            if (hasPrefix)
            {
                if (urlImage.StartsWith(S_URL_PREFIX))
                {
                    pos = S_URL_PREFIX.Length;
                }
                else
                {
                    return props;
                }
            }

            string type = null;
            string host;
            int port = 0;
            string database;
            string path;
            bool isNetwork = false;

            props.setProperty("url", url);

            int semicolpos = url.IndexOf(';', pos);

            if (semicolpos < 0)
            {
                semicolpos = url.Length;
            }
            else
            {
                arguments = urlImage.Substring( semicolpos + 1 ,  urlImage.Length - (semicolpos + 1) );
                extraProps = EffiProzProperties.delimitedArgPairsToProps(arguments,
                        "=", ";", null);

                //todo - check if properties have valid names / values
                props.addProperties(extraProps);
            }

            if (semicolpos == pos + 1 && urlImage.Substring(pos).StartsWith(S_DOT))
            {
                type = S_DOT;
            }
            else if (urlImage.Substring(pos).StartsWith(S_MEM))
            {
                type = S_MEM;
            }
            else if (urlImage.Substring(pos).StartsWith(S_FILE))
            {
                type = S_FILE;
            }
            else if (urlImage.Substring(pos).StartsWith(S_RES))
            {
                type = S_RES;
            }
            else if (urlImage.Substring(pos).StartsWith(S_ALIAS))
            {
                type = S_ALIAS;
            }
            //else if (urlImage.StartsWith(S_HSQL))
            //{
            //    type = S_HSQL;
            //    port = ServerConstants.SC_DEFAULT_HSQL_SERVER_PORT;
            //    isNetwork = true;
            //}
            //else if (urlImage.Substring(pos).StartsWith(S_HSQLS))
            //{
            //    type = S_HSQLS;
            //    port = ServerConstants.SC_DEFAULT_HSQLS_SERVER_PORT;
            //    isNetwork = true;
            //}
            //else if (urlImage.Substring(pos).StartsWith(S_HTTP))
            //{
            //    type = S_HTTP;
            //    port = ServerConstants.SC_DEFAULT_HTTP_SERVER_PORT;
            //    isNetwork = true;
            //}
            //else if (urlImage.Substring(pos).StartsWith(S_HTTPS))
            //{
            //    type = S_HTTPS;
            //    port = ServerConstants.SC_DEFAULT_HTTPS_SERVER_PORT;
            //    isNetwork = true;
            //}

            if (type == null)
            {
                type = S_FILE;
            }
            else if (type == S_DOT)
            {
                type = S_MEM;

                // keep pos
            }
            else
            {
                pos += type.Length;
            }

            props.setProperty("connection_type", type);

            if (isNetwork)
            {
                int slashpos = url.IndexOf('/', pos);

                if (slashpos < pos || slashpos > semicolpos)
                {
                    slashpos = semicolpos;
                }

                int colonpos = url.IndexOf(':', pos);

                if (colonpos < pos || colonpos > slashpos)
                {
                    colonpos = slashpos;
                }
                else
                {
                    try
                    {
                        port = int.Parse(url.Substring(colonpos + 1,
                                                              slashpos));
                    }
                    catch (FormatException )
                    {
                        return null;
                    }
                }

                host = urlImage.Substring( pos ,  colonpos - (pos) );

                int secondslashpos = url.LastIndexOf('/', semicolpos);

                if (secondslashpos < pos)
                {
                    path = "/";
                    database = "";
                }
                else if (secondslashpos == slashpos)
                {
                    path = "/";
                    database = urlImage.Substring( secondslashpos + 1 ,  semicolpos - (secondslashpos + 1) );
                }
                else
                {
                    path = url.Substring( slashpos ,  secondslashpos - (slashpos) );
                    database = urlImage.Substring( secondslashpos + 1 ,  semicolpos - (secondslashpos + 1) );
                }

                props.setProperty("port", port);
                props.setProperty("host", host);
                props.setProperty("path", path);

                if (extraProps != null)
                {
                    string filePath = extraProps.getProperty("filepath");

                    if (filePath != null && database.Length != 0)
                    {
                        database += ";" + filePath;
                    }
                }
            }
            else
            {
                if (type == S_MEM || type == S_RES)
                {
                    database = urlImage.Substring( pos ,  semicolpos - (pos) ).ToLower();

                    if (type == S_RES)
                    {
                        if (database.IndexOf('/') != 0)
                        {
                            database = '/' + database;
                        }
                    }
                }
                else
                {
                    database = url.Substring( pos ,  semicolpos - (pos) );
                }

                if (database.Length == 0)
                {
                    return null;
                }
            }

            props.setProperty("database", database);

            return props;
        }
        /*
            public static void main(String[] argv) {

                parseURL(
                    "JDBC:hsqldb:hsql://myhost:1777/mydb;filepath=c:/myfile/database/db",
                    true);
                parseURL("JDBC:hsqldb:../data/mydb.db", true);
                parseURL("JDBC:hsqldb:../data/mydb.db;ifexists=true", true);
                parseURL("JDBC:hsqldb:HSQL://localhost:9000/mydb", true);
                parseURL(
                    "JDBC:hsqldb:Http://localhost:8080/servlet/org.hsqldb.Servlet/mydb;ifexists=true",
                    true);
                parseURL("JDBC:hsqldb:Http://localhost/servlet/org.hsqldb.Servlet/",
                         true);
                parseURL("JDBC:hsqldb:hsql://myhost", true);
            }
        */
    }
}